/**
 * 返回选中单元格的行、列、宫高亮索引
 * @param {*} index 
 * @returns 
 */
const getHighlight = (index) => {
    let result = new Set
    let size = 9
    let boxSize = Math.sqrt(size)
    let x = index % size
    let y = parseInt(index / size)
    let b = parseInt(y / boxSize) * boxSize + parseInt(x / boxSize)

    for (let i = 0; i < size; i++) {
        result.add(i + y * size)
        result.add(i * size + x)
    }

    let sx = b % boxSize * boxSize
    let sy = parseInt(b / boxSize) * boxSize
    for (let i = 0; i < boxSize; i++) {
        for (let j = 0; j < boxSize; j++) {
            result.add(sx + i + (sy + j) * size)
        }
    }

    return Array.from(result)
}

const CellEntity = {
    id: null,
    value: 0,
    note: [],
    state: 'normal',
    highlight: false,
    samed: false,
    selected: false,
    prompted: false,
    mistaken: false,

    getValue() {
        return this.value > 0 ? this.value : ''
    },
    getNote() {
        let result = []
        if (this.note.length > 0) {
            for (let i = 0; i < 9; i++) {
                if (this.note.includes(i + 1)) {
                    result.push(i + 1)
                } else {
                    result.push('')
                }
            }
        }
        return result
    },
    getClass() {
        let result = []
        if (this.state !== 'normal') {
            result.push(`cell-${this.state}`)
        }
        if (this.highlight) {
            result.push(`cell-highlight`)
        }
        if (this.samed) {
            result.push(`cell-samed`)
        }
        if (this.selected) {
            result.push(`cell-selected`)
        }

        if (this.prompted) {
            result.push(`cell-prompted`)
        } else if (this.mistaken) {
            result.push(`cell-mistaken`)
        }

        if (this.mistaken) {
            result.push(`bg-mistaken`)
        }
        return result
    },
}

export const BoardEntity = {
    // 选中的单元格
    selected: null,
    // 是否开启笔记模式
    active: false,
    // 完整解决方案
    solution: [],
    // 数独题目
    subject: [],
    // 填数
    values: [],
    // 笔记
    notes: [],

    // 已提示的单元格ID数组
    prompts: {},
    // 已出错的单元格ID数组
    mistakes: {},

    // 初始化
    init() {
        this.selected = null
        this.solution = []
        this.subject = []
        this.prompts = {}
        this.mistakes = {}
        this.values = new Array(81).fill(0)
        
        let notes = []
        for (let i = 0; i < 81; i++) {
            notes.push([])
        }
        this.notes = notes
    },

    // 判断是否已选中单元格
    getIsSelected() {
        return this.selected !== null
    },
    // 是否允许输入，1、判断是否选中单元格；2、选中单元格不是题目数值；3、选中单元格不是提示数值
    getAllowInput(selected = null) {
        selected = selected !== null ? selected : this.selected
        return selected !== null && this.subject[selected] === 0 && this.prompts[selected] === undefined
    },
    // 切换输入笔记模式
    toggle() {
        this.active = !this.active
    },
    // 返回所有单元格对象列表
    getCells() {
        let cells = []
        let highlight = this.selected !== null ? getHighlight(this.selected) : []
    
        for (let i = 0; i < 81; i++) {
            let cell = Object.assign({}, CellEntity)
    
            cell.id = i
    
            if (this.subject[i] > 0) {
                cell.value = this.subject[i]
                cell.state = 'subject'
            } else if (this.values[i] > 0) {
                cell.value = this.values[i]
                if (this.values[i] != this.solution[i]) {
                    cell.state = 'incorrect'
                }
            }
    
            if (this.mistakes[i]) {
                cell.mistaken = true
            }
            if (this.prompts[i]) {
                cell.value = this.prompts[i]
                cell.prompted = true
            }
    
            cell.note = this.notes[i]
    
            if (highlight.includes(i)) {
                cell.highlight = true
            }
    
            if (this.selected === i) {
                cell.selected = true
            }
    
            cells.push(cell)
        }

        if (this.getIsSelected()) {
            let samedValue = cells[this.selected].value
            if (samedValue > 0) {
                cells.map(cell => {
                    if (cell.value === samedValue) {
                        cell.samed = true
                    }
                })
            }
        }
    
        return cells
    },
    // 返回所有单元格的值
    getValues() {
        return this.getCells().map(cell => cell.value)
    },
    // 返回指定单元格对象
    getCell(selected = null) {
        selected = selected !== null ? selected : this.selected
        return selected !== null ? this.getCells()[selected] : null
    },

    // 设置指定单元格的值
    setValue(number, selected = null) {
        selected = selected !== null ? selected : this.selected
        if (this.getAllowInput(selected) && this.values[selected] !== number) {
            this.values[selected] = number

            // 移除高亮单元格笔记数值
            this.getCells().map(cell => {
                if (cell.highlight) {
                    this.popNode(number, cell.id)
                }
            })

            return true
        }
        return false
    },
    // 移除指定单元格的值
    removeValue(selected = null) {
        selected = selected !== null ? selected : this.selected
        if (this.getAllowInput(selected)) {
            this.values[selected] = 0
            return true
        }
        return false
    },
    // 设置指定单元格的值或笔记
    set(number, selected = null) {
        selected = selected !== null ? selected : this.selected
        if (this.active) {
            return this.setNote(number, selected)
        } else {
            return this.setValue(number, selected)
        }
    },

    // 增加指定单元格的笔记数值
    appendNode(number, selected = null) {
        selected = selected !== null ? selected : this.selected
        if (this.getAllowInput(selected)) {
            let note = this.notes[selected]
            if (note.indexOf(number) === -1) {
                note.push(number)
                return true
            }
        }
        return false
    },
    // 移除指定单元格的笔记数值
    popNode(number, selected = null) {
        selected = selected !== null ? selected : this.selected
        if (this.getAllowInput(selected)) {
            let note = this.notes[selected]
            let index = note.indexOf(number)
            if (index !== -1) {
                note.splice(index, 1)
                return true
            }
        }
        return false
    },
    // 增加或移除指定单元格的笔记数值
    setNote(number, selected = null) {
        if (this.appendNode(number, selected)) {
            return true
        } else if (this.popNode(number, selected)) {
            return true
        }
        return false
    },

    // 清空指定单元格的笔记
    removeNote(selected = null) {
        selected = selected !== null ? selected : this.selected
        if (this.getAllowInput(selected)) {
            this.notes[selected] = []
            return true
        }
        return false
    },
    // 清空指定单元格的值和笔记
    remove(selected = null) {
        selected = selected !== null ? selected : this.selected
        if (this.getAllowInput(selected)) {
            this.removeNote(selected)
            this.removeValue(selected)
            return true
        }
        return false
    },
    // 提示
    prompt(selected = null) {
        selected = selected !== null ? selected : this.selected
        if (this.getAllowInput(selected)) {
            if (!this.prompts[selected]) {
                this.prompts[selected] = this.solution[selected]

                // 移除高亮单元格笔记数值
                this.getCells().map(cell => {
                    if (cell.highlight) {
                        this.popNode(this.solution[selected], cell.id)
                    }
                })

                return true
            }
        }
        return false
    },
    // 验证指定单元格是否正确
    validate(selected = null) {
        selected = selected !== null ? selected : this.selected
        let cell = this.getCell(selected)
        return cell && cell.value === this.solution[selected]
    },
    // 检测所有单元格是否填写完整并确保填写值正确性
    getIsCompleted() {
        return this.getValues().every((value, i) => {
            return value > 0 && value === this.solution[i]
        })
    },
}
